Added ssl_handshake_step() to allow single stepping the handshake process Single stepping the handshake process allows for better support of non-blocking network stacks and for getting information from specific handshake messages if wanted. 
diff --git a/ChangeLog b/ChangeLog index 701f86b..f590069 100644 --- a/ChangeLog +++ b/ChangeLog 
@@ -1,6 +1,9 @@  PolarSSL ChangeLog    = Version Master +Changes + * Added ssl_handshake_step() to allow single stepping the handshake process +  Bugfix  * Memory leak when using RSA_PKCS_V21 operations fixed  * Handle future version properly in ssl_write_certificate_request() 
diff --git a/include/polarssl/ssl.h b/include/polarssl/ssl.h index e5d9eb7..9746e27 100644 --- a/include/polarssl/ssl.h +++ b/include/polarssl/ssl.h 
@@ -971,6 +971,20 @@  int ssl_handshake( ssl_context *ssl );    /** + * \brief Perform a single step of the SSL handshake + * + * Note: the state of the context (ssl->state) will be at + * the following state after execution of this function. + * Do not call this function if state is SSL_HANDSHAKE_OVER. + * + * \param ssl SSL context + * + * \return 0 if successful, POLARSSL_ERR_NET_WANT_READ, + * POLARSSL_ERR_NET_WANT_WRITE, or a specific SSL error code. + */ +int ssl_handshake_step( ssl_context *ssl ); + +/**  * \brief Perform an SSL renegotiation on the running connection  *  * \param ssl SSL context @@ -1061,8 +1075,8 @@  /*  * Internal functions (do not call directly)  */ -int ssl_handshake_client( ssl_context *ssl ); -int ssl_handshake_server( ssl_context *ssl ); +int ssl_handshake_client_step( ssl_context *ssl ); +int ssl_handshake_server_step( ssl_context *ssl );  void ssl_handshake_wrapup( ssl_context *ssl );    int ssl_send_fatal_handshake_failure( ssl_context *ssl ); 
diff --git a/library/ssl_cli.c b/library/ssl_cli.c index 42ddf41..545906a 100644 --- a/library/ssl_cli.c +++ b/library/ssl_cli.c 
@@ -1274,121 +1274,113 @@  }    /* - * SSL handshake -- client side + * SSL handshake -- client side -- single step  */ -int ssl_handshake_client( ssl_context *ssl ) +int ssl_handshake_client_step( ssl_context *ssl )  {  int ret = 0;   - SSL_DEBUG_MSG( 2, ( "=> handshake client" ) ); + if( ssl->state == SSL_HANDSHAKE_OVER ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );   - while( ssl->state != SSL_HANDSHAKE_OVER ) + SSL_DEBUG_MSG( 2, ( "client state: %d", ssl->state ) ); + + if( ( ret = ssl_flush_output( ssl ) ) != 0 ) + return( ret ); + + switch( ssl->state )  { - SSL_DEBUG_MSG( 2, ( "client state: %d", ssl->state ) ); - - if( ( ret = ssl_flush_output( ssl ) ) != 0 ) + case SSL_HELLO_REQUEST: + ssl->state = SSL_CLIENT_HELLO;  break;   - switch( ssl->state ) - { - case SSL_HELLO_REQUEST: - ssl->state = SSL_CLIENT_HELLO; - break; + /* + * ==> ClientHello + */ + case SSL_CLIENT_HELLO: + ret = ssl_write_client_hello( ssl ); + break;   - /* - * ==> ClientHello - */ - case SSL_CLIENT_HELLO: - ret = ssl_write_client_hello( ssl ); - break; + /* + * <== ServerHello + * Certificate + * ( ServerKeyExchange ) + * ( CertificateRequest ) + * ServerHelloDone + */ + case SSL_SERVER_HELLO: + ret = ssl_parse_server_hello( ssl ); + break;   - /* - * <== ServerHello - * Certificate - * ( ServerKeyExchange ) - * ( CertificateRequest ) - * ServerHelloDone - */ - case SSL_SERVER_HELLO: - ret = ssl_parse_server_hello( ssl ); - break; + case SSL_SERVER_CERTIFICATE: + ret = ssl_parse_certificate( ssl ); + break;   - case SSL_SERVER_CERTIFICATE: - ret = ssl_parse_certificate( ssl ); - break; + case SSL_SERVER_KEY_EXCHANGE: + ret = ssl_parse_server_key_exchange( ssl ); + break;   - case SSL_SERVER_KEY_EXCHANGE: - ret = ssl_parse_server_key_exchange( ssl ); - break; + case SSL_CERTIFICATE_REQUEST: + ret = ssl_parse_certificate_request( ssl ); + break;   - case SSL_CERTIFICATE_REQUEST: - ret = ssl_parse_certificate_request( ssl ); - break; + case SSL_SERVER_HELLO_DONE: + ret = ssl_parse_server_hello_done( ssl ); + break;   - case SSL_SERVER_HELLO_DONE: - ret = ssl_parse_server_hello_done( ssl ); - break; + /* + * ==> ( Certificate/Alert ) + * ClientKeyExchange + * ( CertificateVerify ) + * ChangeCipherSpec + * Finished + */ + case SSL_CLIENT_CERTIFICATE: + ret = ssl_write_certificate( ssl ); + break;   - /* - * ==> ( Certificate/Alert ) - * ClientKeyExchange - * ( CertificateVerify ) - * ChangeCipherSpec - * Finished - */ - case SSL_CLIENT_CERTIFICATE: - ret = ssl_write_certificate( ssl ); - break; + case SSL_CLIENT_KEY_EXCHANGE: + ret = ssl_write_client_key_exchange( ssl ); + break;   - case SSL_CLIENT_KEY_EXCHANGE: - ret = ssl_write_client_key_exchange( ssl ); - break; + case SSL_CERTIFICATE_VERIFY: + ret = ssl_write_certificate_verify( ssl ); + break;   - case SSL_CERTIFICATE_VERIFY: - ret = ssl_write_certificate_verify( ssl ); - break; + case SSL_CLIENT_CHANGE_CIPHER_SPEC: + ret = ssl_write_change_cipher_spec( ssl ); + break;   - case SSL_CLIENT_CHANGE_CIPHER_SPEC: - ret = ssl_write_change_cipher_spec( ssl ); - break; + case SSL_CLIENT_FINISHED: + ret = ssl_write_finished( ssl ); + break;   - case SSL_CLIENT_FINISHED: - ret = ssl_write_finished( ssl ); - break; + /* + * <== ChangeCipherSpec + * Finished + */ + case SSL_SERVER_CHANGE_CIPHER_SPEC: + ret = ssl_parse_change_cipher_spec( ssl ); + break;   - /* - * <== ChangeCipherSpec - * Finished - */ - case SSL_SERVER_CHANGE_CIPHER_SPEC: - ret = ssl_parse_change_cipher_spec( ssl ); - break; + case SSL_SERVER_FINISHED: + ret = ssl_parse_finished( ssl ); + break;   - case SSL_SERVER_FINISHED: - ret = ssl_parse_finished( ssl ); - break; + case SSL_FLUSH_BUFFERS: + SSL_DEBUG_MSG( 2, ( "handshake: done" ) ); + ssl->state = SSL_HANDSHAKE_WRAPUP; + break;   - case SSL_FLUSH_BUFFERS: - SSL_DEBUG_MSG( 2, ( "handshake: done" ) ); - ssl->state = SSL_HANDSHAKE_WRAPUP; - break; + case SSL_HANDSHAKE_WRAPUP: + ssl_handshake_wrapup( ssl ); + break;   - case SSL_HANDSHAKE_WRAPUP: - ssl_handshake_wrapup( ssl ); - break; - - default: - SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) ); - return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); - } - - if( ret != 0 ) - break; - } - - SSL_DEBUG_MSG( 2, ( "<= handshake client" ) ); + default: + SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) ); + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); + }    return( ret );  } -  #endif 
diff --git a/library/ssl_srv.c b/library/ssl_srv.c index 3825393..df57cb3 100644 --- a/library/ssl_srv.c +++ b/library/ssl_srv.c 
@@ -1293,121 +1293,113 @@  }    /* - * SSL handshake -- server side + * SSL handshake -- server side -- single step  */ -int ssl_handshake_server( ssl_context *ssl ) +int ssl_handshake_server_step( ssl_context *ssl )  {  int ret = 0;   - SSL_DEBUG_MSG( 2, ( "=> handshake server" ) ); + if( ssl->state == SSL_HANDSHAKE_OVER ) + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );   - while( ssl->state != SSL_HANDSHAKE_OVER ) + SSL_DEBUG_MSG( 2, ( "server state: %d", ssl->state ) ); + + if( ( ret = ssl_flush_output( ssl ) ) != 0 ) + return( ret ); + + switch( ssl->state )  { - SSL_DEBUG_MSG( 2, ( "server state: %d", ssl->state ) ); - - if( ( ret = ssl_flush_output( ssl ) ) != 0 ) + case SSL_HELLO_REQUEST: + ssl->state = SSL_CLIENT_HELLO;  break;   - switch( ssl->state ) - { - case SSL_HELLO_REQUEST: - ssl->state = SSL_CLIENT_HELLO; - break; - - /* - * <== ClientHello - */ - case SSL_CLIENT_HELLO: - ret = ssl_parse_client_hello( ssl ); - break; - - /* - * ==> ServerHello - * Certificate - * ( ServerKeyExchange ) - * ( CertificateRequest ) - * ServerHelloDone - */ - case SSL_SERVER_HELLO: - ret = ssl_write_server_hello( ssl ); - break; - - case SSL_SERVER_CERTIFICATE: - ret = ssl_write_certificate( ssl ); - break; - - case SSL_SERVER_KEY_EXCHANGE: - ret = ssl_write_server_key_exchange( ssl ); - break; - - case SSL_CERTIFICATE_REQUEST: - ret = ssl_write_certificate_request( ssl ); - break; - - case SSL_SERVER_HELLO_DONE: - ret = ssl_write_server_hello_done( ssl ); - break; - - /* - * <== ( Certificate/Alert ) - * ClientKeyExchange - * ( CertificateVerify ) - * ChangeCipherSpec - * Finished - */ - case SSL_CLIENT_CERTIFICATE: - ret = ssl_parse_certificate( ssl ); - break; - - case SSL_CLIENT_KEY_EXCHANGE: - ret = ssl_parse_client_key_exchange( ssl ); - break; - - case SSL_CERTIFICATE_VERIFY: - ret = ssl_parse_certificate_verify( ssl ); - break; - - case SSL_CLIENT_CHANGE_CIPHER_SPEC: - ret = ssl_parse_change_cipher_spec( ssl ); - break; - - case SSL_CLIENT_FINISHED: - ret = ssl_parse_finished( ssl ); - break; - - /* - * ==> ChangeCipherSpec - * Finished - */ - case SSL_SERVER_CHANGE_CIPHER_SPEC: - ret = ssl_write_change_cipher_spec( ssl ); - break; - - case SSL_SERVER_FINISHED: - ret = ssl_write_finished( ssl ); - break; - - case SSL_FLUSH_BUFFERS: - SSL_DEBUG_MSG( 2, ( "handshake: done" ) ); - ssl->state = SSL_HANDSHAKE_WRAPUP; - break; - - case SSL_HANDSHAKE_WRAPUP: - ssl_handshake_wrapup( ssl ); - break; - - default: - SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) ); - return( POLARSSL_ERR_SSL_BAD_INPUT_DATA ); - } - - if( ret != 0 ) + /* + * <== ClientHello + */ + case SSL_CLIENT_HELLO: + ret = ssl_parse_client_hello( ssl );  break; + + /* + * ==> ServerHello + * Certificate + * ( ServerKeyExchange ) + * ( CertificateRequest ) + * ServerHelloDone + */ + case SSL_SERVER_HELLO: + ret = ssl_write_server_hello( ssl ); + break; + + case SSL_SERVER_CERTIFICATE: + ret = ssl_write_certificate( ssl ); + break; + + case SSL_SERVER_KEY_EXCHANGE: + ret = ssl_write_server_key_exchange( ssl ); + break; + + case SSL_CERTIFICATE_REQUEST: + ret = ssl_write_certificate_request( ssl ); + break; + + case SSL_SERVER_HELLO_DONE: + ret = ssl_write_server_hello_done( ssl ); + break; + + /* + * <== ( Certificate/Alert ) + * ClientKeyExchange + * ( CertificateVerify ) + * ChangeCipherSpec + * Finished + */ + case SSL_CLIENT_CERTIFICATE: + ret = ssl_parse_certificate( ssl ); + break; + + case SSL_CLIENT_KEY_EXCHANGE: + ret = ssl_parse_client_key_exchange( ssl ); + break; + + case SSL_CERTIFICATE_VERIFY: + ret = ssl_parse_certificate_verify( ssl ); + break; + + case SSL_CLIENT_CHANGE_CIPHER_SPEC: + ret = ssl_parse_change_cipher_spec( ssl ); + break; + + case SSL_CLIENT_FINISHED: + ret = ssl_parse_finished( ssl ); + break; + + /* + * ==> ChangeCipherSpec + * Finished + */ + case SSL_SERVER_CHANGE_CIPHER_SPEC: + ret = ssl_write_change_cipher_spec( ssl ); + break; + + case SSL_SERVER_FINISHED: + ret = ssl_write_finished( ssl ); + break; + + case SSL_FLUSH_BUFFERS: + SSL_DEBUG_MSG( 2, ( "handshake: done" ) ); + ssl->state = SSL_HANDSHAKE_WRAPUP; + break; + + case SSL_HANDSHAKE_WRAPUP: + ssl_handshake_wrapup( ssl ); + break; + + default: + SSL_DEBUG_MSG( 1, ( "invalid state %d", ssl->state ) ); + return( POLARSSL_ERR_SSL_BAD_INPUT_DATA );  }   - SSL_DEBUG_MSG( 2, ( "<= handshake server" ) ); -  return( ret );  } -  #endif 
diff --git a/library/ssl_tls.c b/library/ssl_tls.c index e0a64ab..9411392 100644 --- a/library/ssl_tls.c +++ b/library/ssl_tls.c 
@@ -3513,24 +3513,42 @@  };    /* - * Perform the SSL handshake + * Perform a single step of the SSL handshake  */ -int ssl_handshake( ssl_context *ssl ) +int ssl_handshake_step( ssl_context *ssl )  {  int ret = POLARSSL_ERR_SSL_FEATURE_UNAVAILABLE;   - SSL_DEBUG_MSG( 2, ( "=> handshake" ) ); -  #if defined(POLARSSL_SSL_CLI_C)  if( ssl->endpoint == SSL_IS_CLIENT ) - ret = ssl_handshake_client( ssl ); + ret = ssl_handshake_client_step( ssl );  #endif    #if defined(POLARSSL_SSL_SRV_C)  if( ssl->endpoint == SSL_IS_SERVER ) - ret = ssl_handshake_server( ssl ); + ret = ssl_handshake_server_step( ssl );  #endif   + return( ret ); +} + +/* + * Perform the SSL handshake + */ +int ssl_handshake( ssl_context *ssl ) +{ + int ret = 0; + + SSL_DEBUG_MSG( 2, ( "=> handshake" ) ); + + while( ssl->state != SSL_HANDSHAKE_OVER ) + { + ret = ssl_handshake_step( ssl ); + + if( ret != 0 ) + break; + } +  SSL_DEBUG_MSG( 2, ( "<= handshake" ) );    return( ret );